系列說明 >> 本系列大部分 PR 來自私人或公司專案,部分程式碼、環境設定與實作細節不便公開。
我只能保證文中提到的 PR 與問題皆為真實案例,但會經過必要的匿名化與內容調整。
本系列主要分享問題如何被發現、背後的思考方式,以及解法如何形成,不會深入討論完整實作與部署細節,敬請見諒。
Base Repo: paulsha-cortex
Change Ref: PR #288;背景 Issue #279/Issue #286
Issue: Manager 在單一 Repo 內可以檢查 Ready、派出工作並推進 Lifecycle;改成跨 Repo Fanout 後,Ready 階段與真正派工階段可能從不同 Root 解析 Plan。表面上已 Ready,Canary 卻在 Fanout 前就失去同一份工作定義。
Root Cause: Manager 的工作身分仍隱含綁在單一 Repo;Fanout Plan Pinning 使用了錯誤 Root。Path 看起來只是找檔問題,實際上是 Ready 與 Fanout 沒有共享同一個 Spec Authority。
Solution: 將 Cross-repo Work 的 Plan/Spec Authority 明確 Pin 住,讓 Ready 與 Fanout 沿用同一份 Root 與同一個 Artifact Identity,不再各自從目前所在 Repo 重新猜 Plan。
Evidence: PR #288 與背景 Issues 支持 Cross-repo Canary 的失敗類型、錯誤 Root 與 Plan Authority 修正;對應 regression coverage 驗證 Ready/Fanout 使用同一份 Pinned Spec。這些證據不等於十個 Worker 已完成整批交付,本文只主張 Canary 前的 Authority Path 被修正。
D17 結束後,我很快就把小ma拿去做更大的事。
一份工作拆成十個小 Task。
十個小bu各自等著接活。
小re在旁邊磨刀,準備等第一批結果回來就找洞。
我把 Plan 準備好,先跑 Ready Check。
小ma回:
「Ready。」
很好。
群裡的氣氛立刻像宗門大比前一晚。
人人有 Task,個個有 Worktree,彷彿下一秒就要十路齊發。
然後 Canary 沒出去。
不是第一個 Builder 做錯。
不是 Reviewer 擋下。
甚至還沒走到 Agent 真正開始工作的地方。
Fanout 在找 Plan 時,找不到 Ready 剛才確認過的那一份。
十個小工整整齊齊。
一個都沒開工。
這種場面很像工地開工典禮什麼都有:紅布、剪綵、便當、長官。
只缺施工許可。
症狀非常像一般 Path Bug。
Ready 在 Repo A 成功。
Fanout 到 Repo B 後找不到 Plan。
那把 Path 改對,不就好了?
例如讓 Fanout 多收一個:
--plan /some/absolute/path/spec.md
或者乾脆把 Plan 複製到每個 Target Repo。
乍看之下都能讓 Canary 繼續。
但小re沒有先看 Path。
它問小ma:
「你剛才說 Ready,是拿哪一份 Plan 判的?」
小ma指向管理側的 Spec。
「那 Fanout 現在準備派出去的,又是哪一份?」
另一個 Root 下重新解析出來的 Spec。
群裡安靜了一下。
兩份檔案名稱可能一樣。
內容甚至可能剛好一樣。
但只要它們不是同一個被 Pin 住的 Artifact,Ready 與 Fanout 就不是在回答同一個問題。
前面說:
這份 Plan 已經 Ready。
後面真正做的卻可能是:
好,那我現在再找一份看起來同名的 Plan 來派工。
這不是 Path 偶爾迷路。
是 Authority 中途換人。
在單一 Repo 裡,這種隱含假設很容易活得很好。
目前 Working Directory、Repo Root、Plan Directory,通常都指向同一棵樹。
所以系統可以偷偷假設:
我現在站在哪個 Repo
≈ Plan 在哪裡
≈ Work 要在哪裡執行
跨 Repo 後,三件事分開了:
Manager 所在 Repo
Plan/Spec 所在 Repo
Builder 要修改的 Target Repo
這時再用「目前 Repo Root」當萬用答案,就會變成:
Ready
→ 從 Manager Root 找 Spec A
→ PASS
Fanout
→ 切到 Target Root
→ 重新找 Spec B
→ 派工
假設 Spec B 不存在,Canary 直接死在門口。
假設 Spec B 存在但已經 Drift,事情更精彩:
Ready 驗的是 A。
Builder 收到的是 B。
最後大家拿著 A 的核准,替 B 的執行背書。
這種錯誤比 file not found 更麻煩。
至少 file not found 很誠實。
它直接告訴你什麼都沒做。
Drift 則會讓每一層看起來都有工作,最後卻沒有兩個人在做同一件事。
D16 的小ma先接走 Job Identity。
D17 又接走 Poll、Gate、Handoff 與 Release。
一路走來,它開始很像群裡那個什麼都管的傢伙。
所以我也很自然地產生一種錯覺:
既然現在由同一個 Manager 負責,Authority 應該就是一致的。
不了。
同一支程式可以很穩定地做錯同一件事。
同一個角色也可以拿錯 Authority,然後非常有紀律地一路錯到底。
小ma的強項是:
Transition 沒完成,就不承認完成。
但它如果一開始綁錯 Plan,後面的堅持只會讓錯誤更有組織。
這一天是小ma第一次正式被自己的 Boundary 打臉。
它很會追狀態。
卻還沒有回答:
這些狀態到底屬於哪一份 Work Definition?
Manager 不是萬能管家。
它也必須服從一個比自己更穩定的 Authority。
不然 Lifecycle 管得愈完整,錯誤 Plan 被執行得也愈完整。
PR #288 修的不是「再補一條 Path」。
真正的改變是把 Plan/Spec Authority Pin 住。
概念上,原本是:
Ready
→ 依目前 Root 解析 Plan
Fanout
→ 換到另一個 Root
→ 再解析一次 Plan
修正後變成:
Pinned Plan Authority
├──> Ready
└──> Fanout
Ready 判斷哪一份 Spec,Fanout 就只能沿用那一份。
不能因為 Target Repo 換了,就順便把 Work Definition 也換掉。
Target Repo 回答的是:
Code 要改在哪裡?
Plan Authority 回答的是:
這次到底要做什麼?
兩件事會一起出現在同一個 Workflow 裡。
但它們不是同一個 Root,也不該由 Working Directory 猜出同一個答案。
這裡一旦分清楚,Cross-repo Fanout 才真的有資格存在。
否則只是把單 Repo 的偶然規則放大到多 Repo,再祈禱每個目錄剛好長得一樣。
前面幾天,這些能力仍然長在 paulshaclaw 裡:
派工。
Job Registry。
Completion Lifecycle。
Handoff。
Downstream Release。
到 Cross-repo 這一步,問題開始明確超出「多開幾隻 Agent」或「Terminal 怎麼管」的範圍。
它真正管理的是:
哪一份 Work Definition 是 Authority
哪些 Repo 只是 Execution Target
哪些 Transition 可以發生
哪些 Artifact 必須一路保持同一身分
這條 Responsibility 後來成為 Cortex 的主場。
但這篇不先把 Cortex 寫成完整產品介紹。
當時我看到的也不是什麼宏大架構。
只是十個小工排好隊,第一隻 Canary 卻因為 Plan Root 對不上,連門都走不出去。
Cortex 不是因為名字比較帥才出場。
是因為 Workflow 開始跨 Repo 後,總得有一個地方對 Work Identity 與 Lifecycle Authority 負責。
名字是後來才穩定的。
責任先被 Failure 逼了出來。
PR #288 修正後,Ready 與 Fanout 對同一份 Pinned Spec 問話。
這能支持:
Cross-repo Canary 不再因為兩個階段各找各的 Plan,而在起跑前失去 Authority。
它不能支持:
十個 Worker 已經全部完成、所有 Repo 都交付成功。
Canary 能出門,只代表路口終於指向同一張地圖。
後面還有 Builder、Reviewer、Gate、Retry、Recovery 與真正的 Completion。
今天先不偷領那些功勞。
不過,Cortex 開始承擔 Cross-repo Lifecycle 後,另一個問題也變得很難忽略。
paulshaclaw 原本既是我操作 Agent 的入口,也開始塞進愈來愈多 Workflow Authority。
一邊是人要看的 Operator Surface。
一邊是系統要守的 Lifecycle State。
兩邊繼續住在一起,真的合理嗎?
老Go看著剛修好的 Cross-repo Flow。
「所以你現在是把管理工作流的腦,塞在操作工具的殼裡?」
「目前是。」
「聽起來下一篇又要搬家。」
我沒有反駁。
因為這次它大概是對的。
Have a nice day.